package com.ideaaedi.mybatis.data.security.support;

import com.ideaaedi.mybatis.data.security.util.BeanUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * 克隆支持
 * <br/>
 * <p>
 *  在某些特定的场景(如存在各种缓存的情况)下，我们希望
 *  <ul>
 *      <li>
 *          入库加密时，加密后的密文不要回写到原来的对象中 —— 因为在某些业务场景下，入库后，后面的程序逻辑还需要取对应的明文值而不是密文值。
 *      </li>
 *      <li>
 *          出库解密时，解密后的明文不要回写到原来的对象中 —— 因为在某些业务场景下，出库后，后面的一些程序逻辑需要修改查询出来的对象的属性值，
 *          你这里修改后；当别人使用相同的sql查询时，因为某些缓存机制的存在，就可能导致别人查出来的对象就是你现在在操作着的对象，随意这个对象
 *          的值比起数据库数据来说，是"失真"了的
 *      </li>
 *  </ul>
 *
 *
 * @author JustryDeng
 * @since 2021/7/18 19:58:14
 */
public interface PojoCloneable<T extends PojoCloneable<T>> {
    
    /**
     * 克隆当前对象
     *
     * @return  （以当前对象）克隆出来的对象
     */
    T clonePojo();
    
    /**
     * 从克隆对象中获取字段值，赋值给当前对象中那些值为null的字段
     *
     * @param clonePojoList（以当前对象）克隆出来的对象
     *   <p>
     *       注：因为{@link EncryptParser.EncryptOop#pojoEncryp}中使用了map来维护原pojo与克隆pojo的关系，
     *           所以 PojoCloneable的实现类最好不要重写equals与hashcode方法， 否则，可能导致某些场景下，部分回填失败，
     *           <br /><br />
     *           详见单元测试，复现步骤： <br />1. 解开 {@link CloneSupportEmployee}上的@EqualsAndHashCode
     *                                 <br />2. 运行com.ideaaedi.mybatis.data.security.test.mybatis.Tests4Backfill#testCloneBackfill3()方法观察输出的id
     *   </p>
     *   <br />
     *   <p>
     *       注：因为可能会clone多次，所以这里按照clone出来的先后顺序（，越后clone出来的会放在越后面），依次放进次list中
     *           此对象即为mybatis入库时操作的clone对象
     *   </p>
     */
    default void handleNullPropertyByClonePojo(List<T> clonePojoList) {
        if (CollectionUtils.isEmpty(clonePojoList)) {
            return;
        }
        int size = clonePojoList.size();
        for (int i = size - 1; i >= 0; i--) {
            T clonePojo = clonePojoList.get(i);
            String[] nonNullPropertyName = BeanUtil.getNonNullPropertyName(this);
            if (nonNullPropertyName.length  == 0) {
                break;
            }
            BeanUtils.copyProperties(clonePojo, this, nonNullPropertyName);
        }
    }
}
